package httpsrv

import (
	"bufio"
	"bytes"
	"chatgpt/logq"
	"errors"
	"github.com/slclub/go-tips"
	"github.com/slclub/go-tips/json"
	"github.com/slclub/go-tips/stringbyte"
	"io"
	"net/http"
)

type HttpPostStream struct {
	Response        http.ResponseWriter
	request         *http.Request
	Url             string
	Data            map[string]any
	ResponseResult  io.Writer
	handleReadFunc  func(obj *HttpPostStream, response *http.Response, buff *bufio.Reader) []byte
	Method          string
	client          http.Client
	ExplainDataFunc func([]byte) (string, int)
	TokenDataFunc   func(int)
}

func NewHttpPostStreamBoth(response http.ResponseWriter, url string, data map[string]any, dest io.Writer) *HttpPostStream {
	obj := &HttpPostStream{
		client:         http.Client{},
		Response:       response,
		Url:            url,
		Data:           data,
		Method:         http.MethodPost,
		ResponseResult: dest,
	}
	if response == nil {
		obj.SetHandleRead(HandleBody)
	}
	return obj
}

func (self *HttpPostStream) Handle() error {
	resp, err := self.client.Do(self.request)
	//resp, err := http.Post(address, "application/json", bytes.NewBuffer(data_code))

	if err != nil {
		logq.Error("[POST][REQ]" + err.Error())
		return err
	}
	defer resp.Body.Close()
	buff := bufio.NewReader(resp.Body)

	//scanner := bufio.NewScanner(resp.Body)
	//scanner.Split(bufio.ScanWords)
	rtn_str := self.handleRead(resp, buff)
	if resp.StatusCode != http.StatusOK {
		rtn_str = []byte(resp.Status)
		err = errors.New(resp.Status)
	}

	if !tips.IsNil(self.ResponseResult) {
		self.ResponseResult.Write(rtn_str)
	}
	flush(self.Response)

	return err
}

func (self *HttpPostStream) Head(fn func(request http.Header, response http.Header)) *HttpPostStream {
	var resH http.Header
	if self.Response != nil {
		resH = self.Response.Header()
		resH.Set("Content-Type", "text/event-stream; charset=utf-8")
		resH.Set("Cache-Control", "no-cache")
		resH.Set("Connection", "keep-alive")
		resH.Set("Keep-Alive", "timeout=5")
		resH.Set("Transfer-Encoding", "chunked")
	}

	data_code, _ := json.Marshal(self.Data)
	reqest, _ := http.NewRequest(self.Method, self.Url, bytes.NewBuffer(data_code))
	reqest.Header.Set("Content-Type", "application/json")
	reqest.Header.Set("Accept", "*/*")
	reqest.Header.Set("Cache-Control", "no-cache")
	reqest.Header.Set("Connection", "keep-alive")
	self.request = reqest
	if fn != nil {
		fn(self.request.Header, resH)
	}
	return self
}

func (self *HttpPostStream) handleRead(resp *http.Response, buff *bufio.Reader) []byte {
	return self.handleReadFunc(self, resp, buff)
}

func (self *HttpPostStream) SetHandleRead(fn func(obj *HttpPostStream, resp *http.Response, buff *bufio.Reader) []byte) *HttpPostStream {
	self.handleReadFunc = fn
	return self
}

// handle funcs
func HandleBody(obj *HttpPostStream, resp *http.Response, buff *bufio.Reader) []byte {
	rtn_byte := []byte{}
	if resp.StatusCode == http.StatusOK {
		for {
			evb, _, err := buff.ReadLine()
			if err != nil || err == io.EOF {
				break
			}
			event_data := string(evb)
			//event_data := scanner.Text()
			//if event_data == "" {
			//	continue
			//}
			rtn_byte = append(rtn_byte, event_data[:]...)
		}
		if obj.ExplainDataFunc != nil {
			rtn_str, tokens := obj.ExplainDataFunc(rtn_byte)
			rtn_byte = stringbyte.StringToBytes(rtn_str)
			if obj.TokenDataFunc != nil {
				obj.TokenDataFunc(tokens)
			}
		}
	} else {
		rtn_byte = []byte(resp.Status)
	}
	return rtn_byte
}

func HandleStreamBody1(obj *HttpPostStream, resp *http.Response, buff *bufio.Reader) []byte {
	rtn_str := []byte{}
	if resp.StatusCode == http.StatusOK {
		for {
			evb, _, err := buff.ReadRune()
			if err != nil || err == io.EOF {
				break
			}
			event_data := string(evb)

			rtn_str = append(rtn_str, event_data[:]...)
			writeWithResponse(obj.Response, []byte(event_data), obj.ResponseResult)
		}
	} else {
		rtn_str = []byte(resp.Status)
	}
	return rtn_str
}

func HandleStreamBody2(obj *HttpPostStream, resp *http.Response, buff *bufio.Reader) []byte {
	rtn_str := []byte{}
	if resp.StatusCode == http.StatusOK {
		for {
			evb, _, err := buff.ReadLine()
			if err != nil || err == io.EOF {
				break
			}
			event_data, tokens := obj.ExplainDataFunc(evb)
			if tokens == 0 {
				continue
			}
			//event_data := string(evb)

			rtn_str = append(rtn_str, event_data[:]...)
			writeWithResponse(obj.Response, []byte(event_data), obj.ResponseResult)
		}
	} else {
		rtn_str = []byte(resp.Status)
	}
	return rtn_str
}
